/**
 * 
 */
package com.novartis.nims.trialsubmitter;

import java.io.File;
import java.net.URI;

import com.novartis.nims.util.RecursiveFileListIterator;
import com.pixelmed.dicom.AttributeTag;

/**
 * @author Josh Snyder
 *
 */
abstract class TrialReader extends TrialSubmitterStage {
	
	protected URI mTrialLocation;
	protected long mTotalBytes = 0;
	

	private AttributeTag mVisitKey;
	private AttributeTag mScanKey;
	private AttributeTag mFileKey;
	private AttributeTag mScanName;
	private String mVisitNameSeparator;
	private String mUnscheduledVisitIdentifier;
	private String mUnscheduledVisitNumber;
	private boolean mVisitsOrganized;
	private boolean mScansOrganized;
	private boolean mAbortOnFileMissingVisitKey;
	private boolean mAbortOnFileMissingScanKey;
	private boolean mAbortOnFileMissingFileKey;
	private boolean mAbortOnFileMissingScanName;
	private boolean mAbortOnFileWithDuplicateFileKey;
	private boolean mAllowBadPixelData;
	private boolean mAbortOnUnspecifiedFileError;
	
	
	TrialReader(TrialSubmitter ts, URI trialLocation) {
		super(ts);
		//validateDirectoryResource(resource);
		mTrialLocation = trialLocation;
	}

	private long calculateTrialSize(File trialFile) throws TrialReaderException {
		long totalBytes = 0;
		
		RecursiveFileListIterator fileIterator = new RecursiveFileListIterator(trialFile);
		while (fileIterator.hasNext() && (!this.isCancelled())) {
			File file = fileIterator.next();
			totalBytes += file.length();
			
			// HACK can't calculate determinate progress, so keep updating with a
			// repeating iteration from 1 to 99
			this.setProgress(Math.max(1, Math.min(99, ((int) totalBytes % 100))));
		}
		
		if (totalBytes >= new File(mTrialLocation).getUsableSpace()) {
			throw new TrialReaderException("Not enough space on partition holding submission " +
					"directory. Try using another partition or drive with at least " + totalBytes +
					" free.");
		}
		
		return totalBytes;
	}
	
	final Trial execute() throws TrialReaderException {
		this.initialize();
		this.setDeterminate(false);
		this.setMessage("Calculating size of subject directories...");

		File trialFile = new File(mTrialLocation);
		mTotalBytes = calculateTrialSize(trialFile);
		
		if (mTotalBytes < 1) {
			throw new TrialReaderException("Couldn't find any files in source directory. " +
					"Try checking the path to the subject directories, or the contents of the " +
					"subject directories.");
		}
		
		
		this.setProgress(0);
		this.setDeterminate(true);
		this.setMessage("Associating image files with clinical trial keys...");
		
		Trial trial = new Trial();
		URI base = trialFile.getParentFile().toURI();
		trial.setBaseLocation(base);

		long currentBytes = 0;
		RecursiveFileListIterator fileIterator = new RecursiveFileListIterator(trialFile);
		while (fileIterator.hasNext() && (!this.isCancelled())) {
			File file = fileIterator.next();
			URI absoluteLocation = file.toURI();
			URI relativeLocation = base.relativize(absoluteLocation);
			
			SubmissionKey key = this.processFile(file, relativeLocation);
			if (key == null) continue;

			trial.insertImage(absoluteLocation.hashCode(), relativeLocation, key);
			
			// TODO update to determine progress by numfiles, not num bytes?
			currentBytes += file.length();
			setProgress((int) Math.min(100, 100 * currentBytes / mTotalBytes));
		}
		
		return trial;
	}
	
	abstract protected void initialize();

	abstract protected SubmissionKey processFile(File file, URI relativeLocation) throws TrialReaderException;

	//return false if there was an error parsing visit string
	protected boolean parseVisitString(String visitString, StringBuilder parsedVisitName, StringBuilder parsedVisitNumber, StringBuilder parsedRepeatVisitNumber) {
		String[] components = visitString.split(this.getVisitNameSeparator());
		
		parsedVisitName.append(components[0]);
		
		if (parsedVisitName.toString().equals(this.getUnscheduledVisitIdentifier())) {
			switch (components.length) {
			case 2:
				parsedVisitNumber.append(this.getUnscheduledVisitNumber());
				parsedRepeatVisitNumber.append(components[1]);
				break;
			case 3:
				parsedVisitNumber.append(components[1].isEmpty() ? this.getUnscheduledVisitNumber() : components[1]);
				parsedRepeatVisitNumber.append(components[2]);
				break;
			default:
				return false;
			}
			
			if (parsedVisitNumber.toString().isEmpty() || parsedRepeatVisitNumber.toString().isEmpty()) {
				return false;
			}
		} else {
			switch (components.length) {
			case 1:
				break;
			case 2:
				parsedVisitNumber.append(components[1]);
				break;
			case 3:
				parsedVisitNumber.append(components[1]);
				parsedRepeatVisitNumber.append(components[2]);
				break;
			default:
				return false;
			}
		}
		
		return true;
	}
	

	boolean getVisitsOrganized() {
		return mVisitsOrganized;
	}

	void setVisitsOrganized(boolean value) {
		mVisitsOrganized = value;
	}

	boolean getScansOrganized() {
		return mScansOrganized;
	}
	
	public void setScansOrganized(boolean value) {
		mScansOrganized = value;
	}

	boolean getAbortOnUnspecifiedFileError() {
		return mAbortOnUnspecifiedFileError;
	}

	void setAbortOnUnspecifiedFileError(boolean value) {
		mAbortOnUnspecifiedFileError = value;
	}

	boolean getAbortOnFileMissingVisitKey() {
		return mAbortOnFileMissingVisitKey;
	}

	void setAbortOnFileMissingVisitKey(boolean value) {
		mAbortOnFileMissingVisitKey = value;
	}

	boolean getAbortOnFileMissingScanKey() {
		return mAbortOnFileMissingScanKey;
	}

	void setAbortOnFileMissingScanKey(boolean value) {
		mAbortOnFileMissingScanKey = value;
	}

	boolean getAbortOnFileMissingFileKey() {
		return mAbortOnFileMissingFileKey;
	}

	void setAbortOnFileMissingFileKey(boolean value) {
		mAbortOnFileMissingFileKey = value;
	}

	boolean getAbortOnFileMissingScanName() {
		return mAbortOnFileMissingScanName;
	}

	void setAbortOnFileMissingScanName(boolean value) {
		mAbortOnFileMissingScanName = value;
	}

	boolean getAbortOnFileWithDuplicateFileKey() {
		return mAbortOnFileWithDuplicateFileKey;
	}

	void setAbortOnFileWithDuplicateFileKey(boolean value) {
		mAbortOnFileWithDuplicateFileKey = value;
	}

	String getUnscheduledVisitIdentifier() {
		return mUnscheduledVisitIdentifier;
	}

	void setUnscheduledVisitIdentifier(String value) {
		mUnscheduledVisitIdentifier = value;
	}

	AttributeTag getVisitKey() {
		return mVisitKey;
	}

	void setVisitKey(AttributeTag value) {
		mVisitKey = value;
	}

	AttributeTag getScanKey() {
		return mScanKey;
	}

	void setScanKey(AttributeTag value) {
		mScanKey = value;
	}

	AttributeTag getFileKey() {
		return mFileKey;
	}

	void setFileKey(AttributeTag value) {
		mFileKey = value;
	}

	AttributeTag getScanName() {
		return mScanName;
	}

	void setScanName(AttributeTag value) {
		mScanName = value;
	}

	void setAllowBadPixelData(boolean value) {
		mAllowBadPixelData = value;
	}

	boolean getAllowBadPixelData() {
		return mAllowBadPixelData;
	}
	
	void setVisitNameSeparator(String value) {
		mVisitNameSeparator = value;
	}
	
	String getVisitNameSeparator() {
		return mVisitNameSeparator;
	}

	void setUnscheduledVisitNumber(String value) {
		mUnscheduledVisitNumber = value;
	}

	String getUnscheduledVisitNumber() {
		return mUnscheduledVisitNumber;
	}
}
